package com.xujl.utilslibrary.validator;

import com.xujl.utilslibrary.data.DataException;
import com.xujl.utilslibrary.data.ListUtils;
import com.xujl.utilslibrary.view.ViewTool;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;

/**
 * Created by xujl on 2018/1/9.
 * 字段验证工具类，使用方法参见
 *
 * @see com.xujl.utilslibrary.data.ValidationTest
 */

public class VaUtils {
    public static final String STRING_CLASS = "java.lang.String";
    public static final String INTEGER_CLASS = "java.lang.Integer";
    public static final String LONG_CLASS = "java.lang.Long";
    public static final String SHORT_CLASS = "java.lang.Short";
    public static final String CHART_CLASS = "java.lang.Character";
    public static final String BYTE_CLASS = "java.lang.Byte";
    public static final String FLOAT_CLASS = "java.lang.Float";
    public static final String DOUBLE_CLASS = "java.lang.Double";
    public static final String BOOLEAN_CLASS = "java.lang.Boolean";
    public static final String LIST_CLASS = "java.util.List";

    public static final String BASE_INTEGER_CLASS = "int";
    public static final String BASE_LONG_CLASS = "long";
    public static final String BASE_SHORT_CLASS = "short";
    public static final String BASE_CHART_CLASS = "char";
    public static final String BASE_BYTE_CLASS = "btye";
    public static final String BASE_FLOAT_CLASS = "float";
    public static final String BASE_DOUBLE_CLASS = "double";
    public static final String BASE_BOOLEAN_CLASS = "boolean";
    private List<VaCallback> mVaCallbacks;

    /**
     * 自定义验证
     *
     * @param vaCallbacks
     */
    public void setVaCallbacks (List<VaCallback> vaCallbacks) {
        mVaCallbacks = vaCallbacks;
    }


    /**
     * 验证顺序首先按VaOrder排序（升序），
     * 然后按照VaNotEmpty->VaSize->VaValue
     * 的顺序进行
     *
     * @param obj
     * @param groups 验证分组，不传时默认验证全部
     * @throws DataException
     */
    public void validatorAll (Object obj, int... groups) throws DataException {
        final Class<?> objClass = obj.getClass();
        //获取所有字段
        final Field[] fields = objClass.getDeclaredFields();
        //获取添加了验证顺序字段排序的map
        final TreeMap<Integer, Field> fieldTreeMap = new TreeMap<>();
        for (Field field : fields) {
            final VaOrder order = field.getAnnotation(VaOrder.class);
            if (order != null) {
                //没有分组验证时验证全部
                if (ListUtils.isEmpty(groups)) {
                    fieldTreeMap.put(order.value(), field);
                    continue;
                }
                //有分组验证时
                final VaGroup group = field.getAnnotation(VaGroup.class);
                if (group == null) {
                    continue;
                }
                final List<Integer> groupList = groupToList(groups);
                final List<Integer> groupList2 = groupToList(group.value());
                groupList.retainAll(groupList2);
                //只验证需要验证的分组,需要验证的分组和实际分组有交集时才验证
                if (!ListUtils.isEmpty(groupList)) {
                    fieldTreeMap.put(order.value(), field);
                }
            }
        }
        //遍历字段
        final Set<Integer> keySet = fieldTreeMap.keySet();
        final Iterator<Integer> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            final Integer key = iterator.next();
            final Field field = fieldTreeMap.get(key);
            //获取字段类型
            final String fieldType = field.getType().getName();
            field.setAccessible(true);
            try {
                switch (fieldType) {
                    case STRING_CLASS:
                    case INTEGER_CLASS:
                    case LONG_CLASS:
                    case SHORT_CLASS:
                    case DOUBLE_CLASS:
                    case FLOAT_CLASS:
                    case BYTE_CLASS:
                    case CHART_CLASS:
                    case BOOLEAN_CLASS:
                    case BASE_INTEGER_CLASS:
                    case BASE_LONG_CLASS:
                    case BASE_SHORT_CLASS:
                    case BASE_DOUBLE_CLASS:
                    case BASE_FLOAT_CLASS:
                    case BASE_BYTE_CLASS:
                    case BASE_CHART_CLASS:
                    case BASE_BOOLEAN_CLASS:
                        validatorEmpty(field, fieldType, obj);
                        validatorSize(field, fieldType, obj);
                        validatorValue(field, fieldType, obj);
                        validatorBoolean(field, fieldType, obj);
                        if (mVaCallbacks != null && mVaCallbacks.size() != 0) {
                            for (VaCallback vaCallback : mVaCallbacks) {
                                vaCallback.validator(field, fieldType, obj);
                            }
                        }
                        break;
                    default:
                        //非基础类型对象时开始递归
                        validatorAll(field.get(obj));
                        break;

                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private List<Integer> groupToList (int[] groups) {
        List<Integer> groupList = new ArrayList<>();
        if (ListUtils.isEmpty(groups)) {
            return groupList;
        }
        for (int group : groups) {
            groupList.add(group);
        }
        return groupList;
    }

    private void validatorBoolean (Field field, String fieldType, Object obj) throws DataException, IllegalAccessException {
        final VaBoolean annotation = field.getAnnotation(VaBoolean.class);
        //没有此注解时退出
        if (annotation == null) {
            return;
        }

        Boolean result = (Boolean) field.get(obj);
        if (result != annotation.value()) {
            throw new DataException(annotation.msg());
        }
    }

    private void validatorEmpty (Field field, String fieldType, Object obj) throws DataException, IllegalAccessException {
        final VaNotEmpty annotation = field.getAnnotation(VaNotEmpty.class);
        //没有此注解时退出
        if (annotation == null) {
            return;
        }
        String value = (String) field.get(obj);
        if (ViewTool.isEmpty(value)) {
            throw new DataException(annotation.msg());
        }
    }

    private void validatorSize (Field field, String fieldType, Object obj) throws DataException, IllegalAccessException {
        final VaSize annotation = field.getAnnotation(VaSize.class);
        //没有此注解时退出
        if (annotation == null) {
            return;
        }

        String result = String.valueOf(field.get(obj));
        if (result.length() < annotation.min() || result.length() > annotation.max()) {
            throw new DataException(annotation.msg());
        }
    }

    private void validatorValue (Field field, String fieldType, Object obj) throws DataException, IllegalAccessException {
        final VaValue annotation = field.getAnnotation(VaValue.class);
        //没有此注解时退出
        if (annotation == null) {
            return;
        }
        final BigDecimal value = new BigDecimal(String.valueOf(field.get(obj)));
        final BigDecimal min = new BigDecimal(String.valueOf(annotation.min()));
        final BigDecimal max = new BigDecimal(String.valueOf(annotation.max()));
        if ((value.compareTo(min) == -1) || (value.compareTo(max) == 1)) {
            throw new DataException(annotation.msg());
        }
    }

    public interface VaCallback {
        void validator (Field field, String fieldType, Object obj) throws DataException, IllegalAccessException;
    }
}
